package test;

import ap.BDDACLWrapper;
import ap.PositionTuple;
import ap.State;
import common.Device;
import common.correspondence.BDDIOTransfer;
import common.correspondence.BDDTransfer;
import i2analysis.Network;
import i2analysis.StateTransfer;
import jdd.bdd.BDD;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import java.util.*;

public class DBconfig {

    static boolean logRecord = true;

    HashMap<String,DeviceConfig> deviceConfigs;
    HashMap<String, Device> devices;
    PositionTuple resultPort;
    PositionTuple countStartPort;
    BDDTransfer bt;
    BDD bdd;
    int packetSpace;


    public DBconfig(){
        deviceConfigs = new HashMap<>();
        devices = new HashMap<>();
        bdd = new BDDACLWrapper().getBDD();
        bt = new BDDIOTransfer(bdd);
    }

    public void addDevice(String name, String c, String rc, String fc){
        DeviceConfig dc = new DeviceConfig(c, rc, fc);
        deviceConfigs.put(name, dc);
    }

    public void closeAll(){
        for(Device d:devices.values()){
            d.close();
        }
    }

    public void createDevices(){
        closeAll();

        devices.clear();

        for(String name:deviceConfigs.keySet()){
            DeviceConfig dc = deviceConfigs.get(name);
            Device d = new Device(dc.config, dc.reqConfig, dc.fwdConfig, false);
            d.setLogRecord(logRecord);
//            d.output = true;
            devices.put(name, d);
        }
        Device d = devices.get(resultPort.getDeviceName());
//        packetSpace = this.bt.stringToBdd(d.bt.bddToString(d.requirementMap.get(0).packetSpace));
        untilAlready();

    }

    public void doCommand(String command){
        for(Device d:devices.values()){
            d.doCommand(command);
        }
    }

    public void doCommand(String device, String command){
        Device d = devices.get(device);
        if(d != null){
            d.doCommand(command);
        }
    }

    public void wait(int millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public boolean checkAlready(){
        for(Device d:devices.values()){
            if(!d.isAlready()) return false;
        }
        return true;
    }

    public void untilAlready(){
        while (!checkAlready()){
            wait(100);
        }
    }

    public void countStart(){
        devices.get(countStartPort.getDeviceName()).doCommand("count 0");
    }

    public Integer getDPVResult(){
        return getDeviceResult(devices.get(resultPort.getDeviceName()));
    }

    public Integer getAPResult() throws IOException {
        HashMap<String, String> deviceConfig = new HashMap<>();
        for(String name:deviceConfigs.keySet()){
            deviceConfig.put(name, "config/" + deviceConfigs.get(name).fwdConfig);
        }
        Network n = new Network("i2", deviceConfig);
        StateTransfer stfer = new StateTransfer(n);
        State hs = stfer.Traverse(resultPort, countStartPort);
        BDDTransfer nbt = new BDDIOTransfer(n.bddengine.getBDD());
        List<String> r = hs.getDstState(nbt);
        int rp = 0;
        for(String s:r){
            rp = bdd.orTo(rp, bt.stringToBdd(s));
        }

        return bdd.andTo(rp, packetSpace);
    }

    public void setFWDConfig(String name, String path){
        deviceConfigs.get(name).fwdConfig = path;
    }

    public Integer getDeviceResult(Device d){
        HashMap<Integer, Vector<Integer>> res = d.getResult();
        if(res == null) return null;
        int r = 0;
        for(Integer p: res.keySet()){
            Vector<Integer> c = res.get(p);
            if(c.size() == 1 && c.get(0) == 0) continue;
            r = bdd.orTo(r, bt.stringToBdd(d.bt.bddToString(p)));
        }
        return r;
    }

    public long getSum(Iterable<Long> iterable){
        long s = 0;
        for(Long i : iterable){
            s += i;
        }
        return s;
    }

    public HashMap<String, Long> getUsage(){
        HashMap<String, Long> memoryUsage = new HashMap<>();
        for(Device d1: devices.values()){
            long u = d1.bddEngine.getBDD().debug_table_size() - d1.bddEngine.getBDD().debug_compute_free_nodes_count();
            memoryUsage.put(d1.name, u);
        }
        return memoryUsage;
    }

    public void setConfigI20(){
        deviceConfigs.clear();
        String reqFile = "i2/requirement.txt";
        addDevice("atla", "i2/atla", reqFile, "i2/atlaap");
        addDevice("chic", "i2/chic", reqFile, "i2/chicap");
        addDevice("hous", "i2/hous", reqFile, "i2/housap");
        addDevice("seat", "i2/seat", reqFile, "i2/seatap");
        addDevice("salt", "i2/salt", reqFile, "i2/saltap");
        addDevice("wash", "i2/wash", reqFile, "i2/washap");
        addDevice("kans", "i2/kans", reqFile, "i2/kansap");
        addDevice("losa", "i2/losa", reqFile, "i2/losaap");
        addDevice("newy32aoa", "i2/newy32aoa", reqFile, "i2/newy32aoaap");
        countStartPort = new PositionTuple("wash", "et-3/0/0-1");
        resultPort = new PositionTuple("seat", "xe-0/0/0");
    }

    public void setConfigI2(){
        deviceConfigs.clear();
        String reqFile = "i2/requirement.txt";
        addDevice("atla", "i2/atla", reqFile, "i2Test/atlaChange");
        addDevice("chic", "i2/chic", reqFile, "i2Test/chicapChange");
        addDevice("hous", "i2/hous", reqFile, "i2Test/housapChange");
        addDevice("seat", "i2/seat", reqFile, "i2Test/seatapChange");
        addDevice("salt", "i2/salt", reqFile, "i2Test/saltapChange");
        addDevice("wash", "i2/wash", reqFile, "i2Test/washapChange");
        addDevice("kans", "i2/kans", reqFile, "i2Test/kansapChange");
        addDevice("losa", "i2/losa", reqFile, "i2Test/losaapChange");
        addDevice("newy32aoa", "i2/newy32aoa", reqFile, "i2Test/newy32aoaapChange");
        countStartPort = new PositionTuple("wash", "et-3/0/0-1");
        resultPort = new PositionTuple("seat", "xe-0/0/0");
    }

    public void setConfigI2p1(){
        deviceConfigs.clear();
        String reqFile = "i2p1/requirement.txt";
        addDevice("atla", "i2p1/atla", reqFile, "i2Test/atlaapChange");
        addDevice("chic", "i2p1/chic", reqFile, "i2Test/chicapChange");
        addDevice("hous", "i2p1/hous", reqFile, "i2Test/housapChange");
        addDevice("seat", "i2p1/seat", reqFile, "i2Test/seatapChange");
        addDevice("salt", "i2p1/salt", reqFile, "i2Test/saltapChange");
        addDevice("wash", "i2p1/wash", reqFile, "i2Test/washapChange");
        addDevice("kans", "i2p1/kans", reqFile, "i2Test/kansapChange");
        addDevice("losa", "i2p1/losa", reqFile, "i2Test/losaapChange");
        addDevice("newy32aoa", "i2p1/newy32aoa", reqFile, "i2Test/newy32aoaapChange");

        countStartPort = new PositionTuple("wash", "et-3/0/0-1");
        resultPort = new PositionTuple("kans", "xe-0/1/0");
    }


    //-------------------------------------------- 失败前一次 -----------------------------------
    public void setConfigI2Pre(){  //第一次
        deviceConfigs.clear();
        String reqFile = "i2/requirement.txt";
        addDevice("atla", "i2/atla", reqFile, "i2Test/atlaapChangePre");
        addDevice("chic", "i2/chic", reqFile, "i2Test/chicapChangePre");
        addDevice("hous", "i2/hous", reqFile, "i2Test/housapChangePre");
        addDevice("seat", "i2/seat", reqFile, "i2Test/seatapChangePre");
        addDevice("salt", "i2/salt", reqFile, "i2Test/saltapChangePre");
        addDevice("wash", "i2/wash", reqFile, "i2Test/washapChangePre");
        addDevice("kans", "i2/kans", reqFile, "i2Test/kansapChangePre");
        addDevice("losa", "i2/losa", reqFile, "i2Test/losaapChangePre");
        addDevice("newy32aoa", "i2/newy32aoa", reqFile, "i2Test/newy32aoaapChangePre");
        countStartPort = new PositionTuple("wash", "et-3/0/0-1");
        resultPort = new PositionTuple("seat", "xe-0/0/0");
    }

    private static class DeviceConfig{
        public String config;
        public String reqConfig;
        public String fwdConfig;

        public DeviceConfig(String c, String rc, String fc){
            config = c;
            reqConfig = rc;
            fwdConfig = fc;
        }
    }



}
